/**
  **************************************************************************
  * @file     main.c
  * @brief    main program
  **************************************************************************
  *                       Copyright notice & Disclaimer
  *
  * The software Board Support Package (BSP) that is made available to
  * download from Artery official website is the copyrighted work of Artery.
  * Artery authorizes customers to use, copy, and distribute the BSP
  * software and its related documentation for the purpose of design and
  * development in conjunction with Artery microcontrollers. Use of the
  * software is governed by this copyright notice and the following disclaimer.
  *
  * THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
  * GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
  * TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
  * STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
  * INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
  * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
  *
  **************************************************************************
  */

#include "at32f415_clock.h"
#include "usb_conf.h"
#include "usb_core.h"
#include "usbd_int.h"
#include "msc_class.h"
#include "msc_desc.h"
#include "flash_fat16.h"
#include "multi_button.h"

/* delay macros */
#define STEP_DELAY_MS                    50

/* at-start led resouce array */


/* delay variable */
static __IO uint32_t fac_us;
static __IO uint32_t fac_ms;

/* support printf function, usemicrolib is unnecessary */
#if (__ARMCC_VERSION > 6000000)
  __asm (".global __use_no_semihosting\n\t");
  void _sys_exit(int x)
  {
    x = x;
  }
  /* __use_no_semihosting was requested, but _ttywrch was */
  void _ttywrch(int ch)
  {
    ch = ch;
  }
  FILE __stdout;
#else
 #ifdef __CC_ARM
  #pragma import(__use_no_semihosting)
  struct __FILE
  {
    int handle;
  };
  FILE __stdout;
  void _sys_exit(int x)
  {
    x = x;
  }
  /* __use_no_semihosting was requested, but _ttywrch was */
  void _ttywrch(int ch)
  {
    ch = ch;
  }
 #endif
#endif

/******************* define button *******************/
typedef enum
{
  USER_BUTTON                            = 0,
  NO_BUTTON                              = 1
} button_type;

#define USER_BUTTON_PIN                  GPIO_PINS_15
#define USER_BUTTON_PORT                 GPIOA
struct Button btnok;
/** @addtogroup AT32F415_periph_examples
  * @{
  */

/** @addtogroup 415_USB_device_virtual_msc_iap USB_device_virtual_msc_iap
  * @{
  */

/* usb global struct define */
otg_core_type otg_core_struct;
void (*pftarget)(void);
void usb_clock48m_select(usb_clk48_s clk_s);
void usb_gpio_config(void);
void usb_low_power_wakeup_config(void);
void button_exint_init(button_type button);
void button_isr(void);
void jump_to_app(uint32_t address);
void usb_re_connect(void);
void at32_button_init(void);
button_type at32_button_press(void);
uint8_t at32_button_state(void);

/* delay function */
void delay_init(void);
void delay_us(uint32_t nus);
void delay_ms(uint16_t nms);
void delay_sec(uint16_t sec);

crm_clocks_freq_type crm_clocks_freq_struct = {0};




uint8_t read_button_GPIO(uint8_t button_id)
{
    // you can share the GPIO read function with multiple Buttons
    switch (button_id) {
        case 1:
            return gpio_input_data_bit_read(GPIOA, GPIO_PINS_15);
        default:
            return 0;
    }
}

void BTNok_DOUBLE_CLICK_Handler(void *btn)
{
    nvic_system_reset();
	
}

void BTNok_SINGLE_CLICK_Handler(void *btn)
{
   if(flash_fat16_get_upgrade_flag() == IAP_SUCCESS&&flash_iap.msc_up_status ==UPGRADE_DONE)
  {
    nvic_system_reset();
  }
}

void key_init()
{

    button_init(&btnok, read_button_GPIO, 0, 1);
    button_attach(&btnok, DOUBLE_CLICK, BTNok_DOUBLE_CLICK_Handler);
	  button_attach(&btnok, SINGLE_CLICK, BTNok_SINGLE_CLICK_Handler);
    // button_attach(&btnok, LONG_PRESS_START, BTNok_LONG_PRESS_START_Handler);
    button_start(&btnok);
}




/**
  * @brief  main function.
  * @param  none
  * @retval none
  */
int main(void)
{
  nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);

  system_clock_config();
  /* enable usb_otgfs1 periph clock */
  crm_periph_clock_enable(CRM_OTGFS1_PERIPH_CLOCK, TRUE);

  /* enable iomux periph clock */
  crm_periph_clock_enable(CRM_IOMUX_PERIPH_CLOCK, TRUE);

  /* enable gpioa periph clock */
  crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);
	
	gpio_pin_remap_config(SWJTAG_GMUX_010, TRUE);
	
	
	gpio_init_type gpio_init_struct;
  gpio_default_para_init(&gpio_init_struct);
	  /* gpio input config */
  gpio_init_struct.gpio_mode = GPIO_MODE_INPUT;
  gpio_init_struct.gpio_pins = GPIO_PINS_15;
  gpio_init_struct.gpio_pull = GPIO_PULL_UP;
  gpio_init(GPIOA, &gpio_init_struct);
	  /* gpio output config */
  gpio_bits_reset(GPIOA, GPIO_PINS_5);

  gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_MODERATE;
  gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
  gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
  gpio_init_struct.gpio_pins = GPIO_PINS_5;
  gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
  gpio_init(GPIOA, &gpio_init_struct);
	
  flash_fat16_init();
   delay_init();
	if(flash_fat16_get_upgrade_flag() == IAP_SUCCESS &&
   (at32_button_press() == NO_BUTTON))
  {
    jump_to_app(flash_iap.flash_app_addr);
  }
	
	
 
	
	
	
	
	//crm_clocks_freq_get(&crm_clocks_freq_struct);
	  /* enable tmr1 clock */
  crm_periph_clock_enable(CRM_TMR1_PERIPH_CLOCK, TRUE);

  /* tmr1 configuration */
  /* time base configuration */
  /* systemclock/14400/10000 = 1hz */
  tmr_base_init(TMR1, 1439, 499);
  tmr_cnt_dir_set(TMR1, TMR_COUNT_UP);

  /* overflow interrupt enable */
  tmr_interrupt_enable(TMR1, TMR_OVF_INT, TRUE);

  /* tmr1 overflow interrupt nvic init */
  //nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
  nvic_irq_enable(TMR1_OVF_TMR10_IRQn, 0, 0);

  /* enable tmr1 */
  tmr_counter_enable(TMR1, TRUE);



  /* usb gpio config */
gpio_default_para_init(&gpio_init_struct);

  /* add user code begin usb_otgfs1_init 1 */

  /* add user code end usb_otgfs1_init 1 */

  /* configure the VBUS pin */
  gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
  gpio_init_struct.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL;
  gpio_init_struct.gpio_mode = GPIO_MODE_INPUT;
  gpio_init_struct.gpio_pins = GPIO_PINS_9;
  gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
  gpio_init(GPIOA, &gpio_init_struct);

  crm_usb_clock_source_select(CRM_USB_CLOCK_SOURCE_PLL);

  crm_usb_clock_div_set(CRM_USB_DIV_3);

  /* enable otgfs irq */
  nvic_irq_enable(OTG_IRQ, 0, 0);

  /* init usb */
  usbd_init(&otg_core_struct,
            USB_FULL_SPEED_CORE_ID,
            USB_ID,
            &msc_class_handler,
            &msc_desc_handler);
  key_init();
  
	while(1)
  {
    flash_fat16_loop_status();
	 
  }
}




uint8_t gpiotemp=0;
uint8_t ledcount=0;
void at32_led_toggle()
{

	if(ledcount<100){
		ledcount++;
	}else{
		ledcount=0;
		if(gpiotemp==0){
		GPIOA->scr = GPIO_PINS_5;
		gpiotemp=1;
	}
    else{
    GPIOA->clr = GPIO_PINS_5;
			gpiotemp=0;
		}
	}
	
    
}


void TMR1_OVF_TMR10_IRQHandler(void)
{
  if(tmr_interrupt_flag_get(TMR1, TMR_OVF_FLAG) != RESET)
  {
    /* add user code... */
    at32_led_toggle();
		button_ticks();
		
    tmr_flag_clear(TMR1, TMR_OVF_FLAG);
  }
}






/**
  * @brief  returns the selected button state
  * @param  none
  * @retval the button gpio pin value
  */
uint8_t at32_button_state(void)
{
  return gpio_input_data_bit_read(USER_BUTTON_PORT, USER_BUTTON_PIN);
}

/**
  * @brief  returns which button have press down
  * @param  none
  * @retval the button have press down
  */
button_type at32_button_press()
{
  static uint8_t pressed = 1;
  /* get button state in at_start board */
  if((pressed == 1) && (at32_button_state() == RESET))
  {
    /* debounce */
    pressed = 0;
    delay_ms(10);
    if(at32_button_state() == RESET)
      return USER_BUTTON;
  }
  else if(at32_button_state() != RESET)
  {
    pressed = 1;
  }
  return NO_BUTTON;
}






void delay_init()
{
  /* configure systick */
  systick_clock_source_config(SYSTICK_CLOCK_SOURCE_AHBCLK_NODIV);
  fac_us = system_core_clock / (1000000U);
  fac_ms = fac_us * (1000U);
}

/**
  * @brief  inserts a delay time.
  * @param  nus: specifies the delay time length, in microsecond.
  * @retval none
  */
void delay_us(uint32_t nus)
{
  uint32_t temp = 0;
  SysTick->LOAD = (uint32_t)(nus * fac_us);
  SysTick->VAL = 0x00;
  SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk ;
  do
  {
    temp = SysTick->CTRL;
  }while((temp & 0x01) && !(temp & (1 << 16)));

  SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
  SysTick->VAL = 0x00;
}

/**
  * @brief  inserts a delay time.
  * @param  nms: specifies the delay time length, in milliseconds.
  * @retval none
  */
void delay_ms(uint16_t nms)
{
  uint32_t temp = 0;
  while(nms)
  {
    if(nms > STEP_DELAY_MS)
    {
      SysTick->LOAD = (uint32_t)(STEP_DELAY_MS * fac_ms);
      nms -= STEP_DELAY_MS;
    }
    else
    {
      SysTick->LOAD = (uint32_t)(nms * fac_ms);
      nms = 0;
    }
    SysTick->VAL = 0x00;
    SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
    do
    {
      temp = SysTick->CTRL;
    }while((temp & 0x01) && !(temp & (1 << 16)));

    SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
    SysTick->VAL = 0x00;
  }
}

/**
  * @brief  inserts a delay time.
  * @param  sec: specifies the delay time, in seconds.
  * @retval none
  */
void delay_sec(uint16_t sec)
{
  uint16_t index;
  for(index = 0; index < sec; index++)
  {
    delay_ms(500);
    delay_ms(500);
  }
}



#ifdef USB_LOW_POWER_WAKUP
/**
  * @brief  usb low power wakeup interrupt config
  * @param  none
  * @retval none
  */
void usb_low_power_wakeup_config(void)
{
  exint_init_type exint_init_struct;

  exint_default_para_init(&exint_init_struct);

  exint_init_struct.line_enable = TRUE;
  exint_init_struct.line_mode = EXINT_LINE_INTERRUPUT;
  exint_init_struct.line_select = OTG_WKUP_EXINT_LINE;
  exint_init_struct.line_polarity = EXINT_TRIGGER_RISING_EDGE;
  exint_init(&exint_init_struct);

  nvic_irq_enable(OTG_WKUP_IRQ, 0, 0);
}

/**
  * @brief  this function handles otgfs wakup interrupt.
  * @param  none
  * @retval none
  */
void OTG_WKUP_HANDLER(void)
{
  exint_flag_clear(OTG_WKUP_EXINT_LINE);
}

#endif


/**
  * @brief  usb re-connect
  * @param  none
  * @retval none
  */
void usb_reset()
{
  delay_ms(1000);
  usbd_disconnect(&otg_core_struct.dev);
  delay_ms(1000);
  usbd_connect(&otg_core_struct.dev);
}

/**
  * @brief  loop status
  * @param  none
  * @retval none
  */
void flash_fat16_loop_status(void)
{
  static uint8_t status = UPGRADE_READY;
  if(status == flash_iap.msc_up_status)
  {
    return;
  }
  else
  {
    status = flash_iap.msc_up_status;
  }

  switch(flash_iap.msc_up_status)
  {
    case UPGRADE_READY:
      flash_fat16_set_name((const uint8_t *)"Ready", 5);
      break;
    case UPGRAGE_ONGOING:
      break;
    case UPGRADE_SUCCESS:
      flash_fat16_set_name((const uint8_t *)"Success", 7);
      flash_iap.msc_up_status = UPGRADE_DONE;
      usb_reset();
      break;
    case UPGRADE_FAILED:
      flash_fat16_set_name((const uint8_t *)"Failed", 6);
      flash_iap.msc_up_status = UPGRADE_DONE;
      usb_reset();
      break;
    case UPGRADE_LARGE:
      flash_fat16_set_name((const uint8_t *)"Large", 5);
      flash_iap.msc_up_status = UPGRADE_DONE;
      usb_reset();
      break;
    case UPGRADE_UNKNOWN:
      flash_fat16_set_name((const uint8_t *)"Unkown", 6);
      break;
    case UPGRADE_DONE:
      break;
    case UPGRADE_JUMP:
      flash_fat16_set_name((const uint8_t *)"Jump", 6);
      delay_ms(1000);
      jump_to_app(flash_iap.write_addr);
      break;
    default:
      break;
  }

}


/**
  * @brief  jump to app
  * @param  none
  * @retval none
  */
void jump_to_app(uint32_t address)
{
  uint32_t stkptr, jumpaddr;
  stkptr = *(uint32_t *)address;
  jumpaddr = *(uint32_t *)(address + sizeof(uint32_t));

  /* disable nvic irq and periph clock, clear pending */
  nvic_irq_disable(OTGFS1_IRQn);

  __NVIC_ClearPendingIRQ(OTGFS1_IRQn);

  crm_periph_clock_enable(CRM_OTGFS1_PERIPH_CLOCK, FALSE);

  crm_periph_reset(CRM_OTGFS1_PERIPH_RESET, TRUE);
  crm_periph_reset(CRM_OTGFS1_PERIPH_RESET, FALSE);

  __set_MSP(stkptr);
  pftarget = (void (*) (void))jumpaddr;
  pftarget();
}

/**
  * @brief  this function handles otgfs interrupt.
  * @param  none
  * @retval none
  */
void OTG_IRQ_HANDLER(void)
{
  usbd_irq_handler(&otg_core_struct);
}

/**
  * @brief  usb delay millisecond function.
  * @param  ms: number of millisecond delay
  * @retval none
  */
void usb_delay_ms(uint32_t ms)
{
  /* user can define self delay function */
  delay_ms(ms);
}

/**
  * @brief  usb delay microsecond function.
  * @param  us: number of microsecond delay
  * @retval none
  */
void usb_delay_us(uint32_t us)
{
  delay_us(us);
}

/**
  * @}
  */

/**
  * @}
  */
